import { BufferGeometryLoader3D, InstancedMesh3D, MeshNormalMaterial3D, Node3D, PerspectiveCamera3D, Scene3D, StatsComponent, WebGLRenderer3D } from 'feng3d-three';
import GUI from 'lil-gui';

const rootPath = '../../';

let camera: PerspectiveCamera3D;
let scene: Scene3D;
let renderer: WebGLRenderer3D;
let stats: StatsComponent;

let mesh: InstancedMesh3D;
const amount = parseInt(window.location.search.slice(1)) || 10;
const count = Math.pow(amount, 3);
const dummy = new Node3D();

init();
animate();

function init()
{
    camera = new Node3D().addComponent('PerspectiveCamera3D', { fov: 60, aspect: window.innerWidth / window.innerHeight, near: 0.1, far: 100 });
    camera.entity.position.set(amount * 0.9, amount * 0.9, amount * 0.9);
    camera.entity.lookAt(0, 0, 0);

    scene = new Node3D().addComponent('Scene3D');

    const loader = new BufferGeometryLoader3D();
    loader.load(`${rootPath}models/json/suzanne_buffergeometry.json`, function (geometry)
    {
        geometry.computeVertexNormals();
        geometry.scale(0.5, 0.5, 0.5);

        const material = new MeshNormalMaterial3D();
        // check overdraw
        // let material = new THREE.MeshBasicMaterial( { color: 0xff0000, opacity: 0.1, transparent: true } );

        mesh = new Node3D().addComponent('InstancedMesh3D', { geometry, material, count });
        mesh.instanceMatrix.setUsage('DynamicDrawUsage'); // will be updated every frame
        scene.entity.addChild(mesh.entity);

        //

        const gui = new GUI();
        gui.add(mesh, 'count', 0, count);
    });

    //

    renderer = scene.addComponent('WebGLRenderer3D', { parameters: { antialias: true } });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    //

    stats = scene.addComponent('StatsComponent');
    document.body.appendChild(stats.dom);

    //

    window.addEventListener('resize', onWindowResize);
}

function onWindowResize()
{
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();

    renderer.setSize(window.innerWidth, window.innerHeight);
}

//

function animate()
{
    requestAnimationFrame(animate);

    render();

    stats.update();
}

function render()
{
    if (mesh)
    {
        const time = Date.now() * 0.001;

        mesh.entity.rotation.x = Math.sin(time / 4);
        mesh.entity.rotation.y = Math.sin(time / 2);

        let i = 0;
        const offset = (amount - 1) / 2;

        for (let x = 0; x < amount; x++)
        {
            for (let y = 0; y < amount; y++)
            {
                for (let z = 0; z < amount; z++)
                {
                    dummy.position.set(offset - x, offset - y, offset - z);
                    dummy.rotation.y = (Math.sin(x / 4 + time) + Math.sin(y / 4 + time) + Math.sin(z / 4 + time));
                    dummy.rotation.z = dummy.rotation.y * 2;

                    dummy.updateMatrix();

                    mesh.setMatrixAt(i++, dummy.matrix);
                }
            }
        }

        mesh.instanceMatrix.needsUpdate = true;
    }

    renderer.render(scene, camera);
}
